#include <stdio.h>
#include <string.h>

#include "TFly_list.h"
#include "script.h"
#include "script_block.h"
#include "script_expression.h"
#include "script_memory.h"
#include "script_stack.h"
#include "script_string.h"
#include "script_func.h"
#include "script_debug.h"
#include "script_keyword.h"

const static char *VERSION = "v1.3.2";

const char *script_version_get(void)
{
	return VERSION;
}

int script_decode(char *str, script_t *script)
{
	int ret = SCRIPT_ERR_NONE;
	if (str == NULL) {
		return SCRIPT_ERR_PARAM;
	}

	struct exp_string_t *exp_str = exp_string_decode(str);
	if (exp_str == NULL) {
		return SCRIPT_ERR_SYNTAX;
	}

	if(script->code){
		int slen = strlen(str)+1;
		if(script->code_buf_len <= script->code_len + slen){
			while(script->code_buf_len <= script->code_len + slen){
				script->code_buf_len += 256;
			}
			char *buf = script_calloc(1,script->code_buf_len);
			strcpy(buf,script->code);
			script_free(script->code);
			script->code = buf;
		}

		strcat(script->code,str);
		strcat(script->code,"\n");
		script->code_len += slen;
	}

	//show_exp_string_t(exp_str);

	if(strcmp(exp_str->str,"}") == 0){
		script_code_block_t *block = script_stack_Pop(script->code_stack);
		if(script_stack_num_get(script->code_stack) == 1){
			SCRIPT_DEBUG_ERR("'}' is not paired");
			ret = SCRIPT_ERR_SYNTAX;
			goto exit;
		}

		// 代码块解析完成后 删除所有变量
		exp_var_struct_clean(block->vars);	

		exp_string_t *tmp = exp_str->next;
		exp_str->next = NULL;
		exp_string_destroy(exp_str);
		exp_str = tmp;
	}

	if(exp_str == NULL){
		return ret;
	}

	const struct script_keyword_t *keyword = find_keyword(exp_str->str);
	if(keyword){
		ret = keyword->parse(keyword,exp_str->next,script);
		goto exit;
	}
	

	exp_node_t *node = expression_decode(exp_str,script);
	if(node == NULL){
		ret = SCRIPT_ERR_SYNTAX;
		goto exit;
	}

	script_code_block_t *block = script_stack_Top(script->code_stack);

	script_exp_t *exp = script_exp_create(script->line,node);
	TFly_list_add(block->exp_list,exp);
	
exit:
	exp_string_destroy(exp_str);
	return ret;
}

script_t *script_script_create(void)
{
	script_default_func_init();
	script_t *script = script_calloc(1,sizeof(script_t));
	if(script == NULL){
		return NULL;
	}

	script->funcs = TFly_list_create();
	if(script->funcs == NULL){
		script_destroy(script);
		return NULL;
	}

	script->includes = TFly_list_create();

	script->code_stack = script_stack_create(1);
	if(script->code_stack == NULL){
		script_destroy(script);
		return NULL;
	}

	script->code = script_calloc(1,512);
	script->code_buf_len = 512;

	script->main_code = script_code_block_create(script);
	script->exec_block = script_exec_block_create(script->main_code);

	script_stack_Push(script->code_stack,script->exec_block);

	return script;
}

int script_parse_from_string(script_t *script, char *str)
{
	exp_string_t *lines = exp_lines_decode(str);
	exp_string_t *line = lines;
	int ret = 0;
	while(line){
		char *buf = uni_strip(line->str);
		if(buf == NULL || strlen(buf) == 0 || buf[0] == '#'){
			line = line->next;
			continue;
		}

		//SCRIPT_DEBUG_DEBUG("Line %d: %s",line,str);
		script->line++;
		ret = script_decode(buf,script);
		if(ret < 0){
			SCRIPT_DEBUG_ERR("script decode err line:%s",line->str);
			goto err;
		}
		line = line->next;
	}
err:
	exp_string_destroy(lines);
	return ret;
}

int script_parse_from_file(script_t *script, const char *file)
{
	if(file == NULL || script == NULL){
		return SCRIPT_ERR_PARAM;
	}

	FILE *fd = fopen(file,"r");
	if(fd == NULL){
		SCRIPT_DEBUG_ERR("Open file %s fail\n",file);
		return SCRIPT_ERR_OPENFILE;
	}

	if(script->name == NULL){
		script_name_set(script,file);
	}

	int num = script_stack_num_get(script->code_stack);
	script_stack_Push(script->code_stack,script->main_code);
	char buf[5120];
	int line = 0;
	int ret;
	while(1){
		line++;
		memset(buf,0,sizeof(buf));
		if(fgets(buf,sizeof(buf),fd) == NULL){
			break;
		}
		
		char *str = uni_strip(buf);
		if(str == NULL || strlen(str) == 0 || str[0] == '#'){
			continue;
		}
		SCRIPT_DEBUG_DEBUG("Line %d: %s",line,str);
		script->line = line;
		ret = script_decode(str,script);
		if(ret < 0){
			SCRIPT_DEBUG_ERR("script \"%s\" decode err line: %d",script->name,line);
			goto err;
		}
	}

	script_stack_Pop(script->code_stack);
	if(num != script_stack_num_get(script->code_stack)){
		SCRIPT_DEBUG_ERR("script \"%s\" '}' is not paired",script->name);
		return SCRIPT_ERR_SYNTAX;
	}
	fclose(fd);

	return SCRIPT_ERR_NONE;

err:
	script_stack_Pop(script->code_stack);
	fclose(fd);
	return SCRIPT_ERR_SYNTAX;

}

script_t *script_load(char *file)
{
	if(file == NULL){
		return NULL;
	}

	script_t *script = script_script_create();

	if(script_parse_from_file(script,file)){
		goto err;
	}
	//show_script(script);

	return script;

err:
	script_destroy(script);
	return NULL;
}

int script_add_global_var(script_t *script, char *var_name,char *value)
{
	if(script == NULL || var_name == NULL){
		SCRIPT_DEBUG_ERR("Param error!");
		return SCRIPT_ERR_PARAM;
	}

	exp_var_t *var;
	if(value == NULL){
		var = exp_var_create(var_name);
	}else{
		var = exp_var_parse(var_name,value);
	}
	if(var == NULL){
		SCRIPT_DEBUG_ERR("global var create fail");
		return SCRIPT_ERR_SYNTAX;
	}
	exp_var_struct_add(script->exec_block->vars,var);
	return SCRIPT_ERR_NONE;
}


exp_node_t *_run_block(script_exec_block_t *e_block, script_stack_t *block_stack)
{
	if(e_block == NULL || block_stack == NULL){
		return NULL;
	}

	int ret = SCRIPT_ERR_NONE;

	script_stack_t *result_stack = script_stack_create(1);
	exp_node_t *result = NULL;


	script_exp_t *st = TFly_list_item_next(e_block->item);
	while(st){
		exp_node_t *exp = st->exp;
		result = expression_calc(exp,block_stack);
		if(result == NULL){
			ret = SCRIPT_ERR_CALC;
			goto err;
		}else if(result->type == EXP_NODE_TYPE_ERR){
			SCRIPT_DEBUG_ERR("Exec error in script:%s line:%d",e_block->script->name,st->line);
			result->type = EXP_NODE_TYPE_CONST_NULL;
		}

		script_stack_Push(result_stack,result);

		if(result->ctrl == EXP_NODE_CTRL_BREAK){
			break;
		}else if(result->ctrl == EXP_NODE_CTRL_RETURN){
			break;
		}else if(result->ctrl == EXP_NODE_CTRL_CONTINUE){
			break;
		}else if(result->ctrl == EXP_NODE_CTRL_BREAKPOINT){
			break;
		}

		st = TFly_list_item_next(e_block->item);
	}

	result = script_stack_Pop(result_stack);
	if(result){
		exp_node_type_e type;
		int value_int;
		char *value_str;
		double d;

		get_exp_node_value(result, &type, &value_str, &value_int,&d);
		if(type == EXP_NODE_TYPE_CONST_INT){
			exp_node_t *_result = exp_node_create(EXP_NODE_TYPE_CONST_INT,(void*)value_int);
			_result->ctrl = result->ctrl;
			exp_node_destroy(result);
			result = _result;
		}else if(type == EXP_NODE_TYPE_CONST_DOUBLE){
			exp_node_t *_result = exp_node_create(EXP_NODE_TYPE_CONST_DOUBLE,&d);
			_result->ctrl = result->ctrl;
			exp_node_destroy(result);
			result = _result;
		}else if(type == EXP_NODE_TYPE_CONST_STRING){
			exp_node_t *_result = exp_node_create(EXP_NODE_TYPE_CONST_STRING,value_str);
			_result->ctrl = result->ctrl;
			exp_node_destroy(result);
			result = _result;
		}
	}else{
		result = exp_node_create(EXP_NODE_TYPE_CONST_NULL,NULL);
	}


	exp_node_t *res = script_stack_Pop(result_stack);
	while(res){
		exp_node_destroy(res);
		res = script_stack_Pop(result_stack);
	}

	script_stack_destroy(result_stack);
	return result;

err:
	SCRIPT_DEBUG_ERR("Exec error in script:%s line:%d",e_block->script->name,st->line);
	script_stack_destroy(result_stack);

	return result;

}

exp_node_t *run_block(script_code_block_t *block, script_stack_t *block_stack,exp_var_t *var)
{
	if(block == NULL || block_stack == NULL){
		return NULL;
	}

	script_exec_block_t *e_block = script_exec_block_create(block);
	if(var){
		exp_var_destroy(e_block->vars);
		e_block->vars = var;
	}

	script_stack_Push(block_stack,e_block);

	exp_node_t *ret = _run_block(e_block,block_stack);

	script_stack_Pop(block_stack);
	script_exec_block_destroy(e_block);

	return ret;

}

exp_var_t *script_var_find(script_stack_t *block_stack, char *var_name)
{
	script_exec_block_t *block;
	exp_var_t *var = NULL;

	int i;
	int num = script_stack_num_get(block_stack);
	for(i = 0; i < num; i++){
		block = script_stack_get(block_stack,-1 - i);
		if(block == NULL){
			return NULL;
		}
		//printf("%s %d block:%p\r\n",__func__,__LINE__,block);
		//show_exp_var(block->vars,0);

		if(block->vars->name && strcmp(block->vars->name,var_name) == 0){
			return block->vars;
		}

		var = exp_var_struct_get_var(block->vars,var_name);
		if(var){
			return var;
		}
	}

	var = script_default_func_var_get(var_name);

	return var;
}

void run_script(script_t *script)
{
	if(script == NULL){
		return;
	}

	if(script->exec_block->item){
		TFly_list_item_destroy(script->exec_block->item);
		script->exec_block->item = TFly_list_get_item(script->main_code->exp_list);
	}
	//script_stack_Push(script->code_stack,script->exec_block);
	exp_node_t *result = _run_block(script->exec_block,script->code_stack);
	if(result){
		exp_node_destroy(result);
	}
}

exp_node_t *script_run_code(script_t *script, char *code)
{
	if(script == NULL || code == NULL){
		return NULL;
	}
	exp_node_t *exp = expression_parse(code,script);
	if(exp == NULL){
		return NULL;
	}
	exp_node_t *result = expression_calc(exp,script->code_stack);
	exp_node_list_destroy(exp);
	return result;
}

int script_run_code_easy(script_t *script, char *code)
{
	exp_node_t *result = script_run_code(script,code);
	if(result == NULL){
		return -1;
	}

	exp_node_destroy(result);
	return 0;
}

int script_change_var_value(script_t *script, char *var_name,char *value)
{
	if(script == NULL || var_name == NULL){
		return -1;
	}

	exp_node_t *result = script_run_code(script,var_name);
	if(result == NULL){
		return SCRIPT_ERR_SYNTAX;
	}
	if(result->type != EXP_NODE_TYPE_VAR){
		exp_node_destroy(result);
		return SCRIPT_ERR_SYNTAX;
	}


	exp_var_t *var = exp_var_parse("new",value);

	int ret_exp = exp_var_copy(var,result->var);

	exp_var_destroy(var);
	exp_node_destroy(result);

	return ret_exp;
}

int script_bind_c_var(script_t *script, char *var_name, script_var_type_e type, void *value)
{
	if(script == NULL || var_name == NULL || value == NULL){
		return SCRIPT_ERR_PARAM;
	}

	exp_node_t *result = script_run_code(script,var_name);
	if(result == NULL){
		return SCRIPT_ERR_SYNTAX;
	}

	exp_var_t *var = exp_c_var_create("new",type,value);
	exp_var_copy(var,result->var);
	exp_var_destroy(var);
	exp_node_destroy(result);
	return 0;

}

int script_name_set(script_t *script, const char *name)
{
	if(script == NULL || name == NULL){
		return -1;
	}

	char *new = script_calloc(1,strlen(name) + 1);
	strcpy(new,name);
	if(script->name){
		script_free(script->name);
	}
	script->name = new;
	return 0;

}

static void include_data_destroy(struct include_data *data)
{
	if(data == NULL){
		return;
	}

	if(data->var_name){
		script_free(data->var_name);
	}

	if(data->block){
		script_exec_block_destroy(data->block);
		data->block = NULL;
	}

	if(data->inc){
		//printf("\033[32m%s %d inc:%p\033[0m\r\n",__func__,__LINE__,data->inc);
		script_include_destroy(data->inc);
	}
	script_free(data);

}

void script_destroy(script_t *script)
{
	if(script == NULL){
		return;
	}

	if(script->name){
		script_free(script->name);
	}

	if(script->main_code){
		script_code_block_destroy(script->main_code);
	}


	if(script->funcs){
		exp_func_t *func = TFly_list_del_first(script->funcs);
		while(func){
			exp_func_destroy(func);
			func = TFly_list_del_first(script->funcs);
		}

		TFly_list_destroy(script->funcs);
	}

	if(script->exec_block){
		script_exec_block_destroy(script->exec_block);
	}


	while(!script_stack_IsEmpty(script->code_stack)){
		script_stack_Pop(script->code_stack);
	}
	if(script->code_stack){
		script_stack_destroy(script->code_stack);
	}

	if(script->code){
		script_free(script->code);
	}

	if(script->includes){
		/*
		script_include_t *include = TFly_list_del_first(script->includes);
		while(include){
			script_include_destroy(include);
			include = TFly_list_del_first(script->includes);
		}
		*/
		struct include_data *data= TFly_list_del_first(script->includes);
		while(data){
			include_data_destroy(data);
			data = TFly_list_del_first(script->includes);
		}

		TFly_list_destroy(script->includes);
	}

	script_free(script);
}

void show_func(script_func_t *func)
{
	if(func == NULL){
		return;
	}

	printf("function name: %s\n",func->name);
	script_code_block_t *block = func->block;
}

void show_script(script_t *script)
{
	if(script == NULL){
		return;
	}

	printf("Script name: %s\n",script->name);
	printf("Var:\n");
	//show_exp_var(script->script_var,1);

	printf("Functions:\n");

	//printf("code: buf_len:%d code len:%d\n",script->code_buf_len,script->code_len);
	//printf("%s\n",script->code);
}

